// helloworld-bdb.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <iostream>
#include <db_cxx.h>

using namespace std;
/* We want to define our own error function to report any errors we encounter.*/
void errCallback (const DbEnv *env, const char *prefix, const char *errMsg)
{
    cout << prefix << " " << errMsg << endl;
}

int _tmain(int argc, _TCHAR* argv[])
{
    /* a: create an environment for our database.
          we need an environment so we can open
          several database tables; which is what
          we eventually intend to do.
    */
    u_int32_t dbEnvFlags = 0;
    DbEnv dbenv(dbEnvFlags);

    try
    {
        // b: define a directory for our environment
        const char *envhome = ".\\MyBDBEnv";

        // c: set a unique prefix for environment error messages
        dbenv.set_errpfx("MyErrorFunction");

        // d: tell the environment to use our error function
        dbenv.set_errcall(errCallback);

        // e: open our environment
        /* DB_INIT_MPOOL: "Initializes the shared memory buffer pool subsystem.
           This subsystem should be used whenever an application is using any
           Berkeley DB access method."

           DB_USE_ENVIRON: "The Berkeley DB process' environment may be permitted
           to specify information to be used when naming files; see Berkeley DB File Naming.
           Because permitting users to specify which files are used can create security
           problems, environment information will be used in file naming for all users
           only if the DB_USE_ENVIRON flag is set.  The directories named in the environment
           variables "DB_HOME = dir" and "TMPDIR = dir"
        */
        dbenv.open(envhome, DB_CREATE | DB_INIT_MPOOL, 0);

        // Now that our environment is open and ready
        // we start working with our first table

        // 1.b: create a database handle in our environment
        Db db(&dbenv, 0);

        // Allow the database to have duplicate records
        db.set_flags(DB_DUPSORT);

        // 2.a: Define parameters for opening database
        DbTxn      *dbTxnID      = NULL;     // we aren't using transactions
        const char *dbFileName   = "ExampleTable";
        const char *dbName       = NULL; // we only want one table in our file

        // 2.b: open our table using the above parameters
        db.open(dbTxnID, dbFileName, dbName, DB_BTREE, DB_CREATE, 0);

        // 3: create the first key to store & calc its length
        char        *first_key = "first_record";
        u_int32_t   key_len = (u_int32_t)strlen(first_key);

        // 4: create data value associated with the first key
        char        *data_value = "0-Hello World-Berkeley DB style!!";
        u_int32_t   value_len = (u_int32_t)strlen(data_value);

        // 5.a: Define parameters for inserting the key/value pair
        DbTxn      *dbPutTxnID  = NULL;     // we aren't using transactions
        Dbt key(first_key, key_len + 1 );
        Dbt value(data_value, value_len + 1);
        u_int32_t   dbPutFlags  = DB_NODUPDATA;
        int ret;

        // 5: insert the key/value pair into database
        ret = db.put(dbPutTxnID, &key, &value, dbPutFlags);

        data_value = "1-Hello World-Berkeley DB style!!";
        Dbt value1(data_value, value_len + 1);
        ret = db.put(dbPutTxnID, &key, &value1, dbPutFlags);
        data_value = "2-Hello World-Berkeley DB style!!";
        Dbt value2(data_value, value_len + 1);
        ret = db.put(dbPutTxnID, &key, &value2, dbPutFlags);
        data_value = "3-Hello World-Berkeley DB style!!";
        Dbt value3(data_value, value_len + 1);
        ret = db.put(dbPutTxnID, &key, &value3, dbPutFlags);

        first_key = "next0_record";
        Dbt key4(first_key, key_len + 1 );
        data_value = "4-Hello World-Berkeley DB style!!";
        Dbt value4(data_value, value_len + 1);
        ret = db.put(dbPutTxnID, &key4, &value4, dbPutFlags);

        // create a cursor
        Dbc * myCursor;
        db.cursor(NULL, &myCursor, 0);
        // 5: define a database value type, get it from the database
        //    and send it to the console.
        //
        Dbt stored_value;
        memset(&stored_value, 0, sizeof(data_value));
        myCursor->get(&key, &stored_value, DB_SET);
        // count duplicates
        db_recno_t myCount;
        memset(&myCount,0, sizeof(db_recno_t));
        myCursor->count(&myCount,0);
        cout << "Number of Duplicate Records: " << myCount << endl;

        // print out first record
        cout << (char *)key.get_data() << ": " << (char *)stored_value.get_data() << endl;

        while ((ret = myCursor->get(&key, &stored_value, DB_NEXT_DUP)) == 0)
            cout << (char *)key.get_data() << ": " << (char *)stored_value.get_data() << endl;

        // 6: close cursor and database
        myCursor->close();
        db.close(0);
    }
    catch(DbException &dbex)
    {
        dbenv.err(dbex.get_errno(), "Db exception caught");
    }
    catch(...)
    {
        cout << "unknown exception caught" << endl;
    }

    return 0;
}